import React, {
  ReactElement,
  useRef,
  useEffect,
  useCallback,
  useMemo,
  useImperativeHandle,
  forwardRef,
  useState
} from 'react';
import { DeviceEventEmitter, View } from 'react-native';

import styles from './style';
import MediaPlayer from '../../../../../../components/MediaPlayer';
import {
  getLocalVideoUrl,
  reportVideoPlayerOnError
} from '../../../../actions';
import {
  CARD_HEIGHT,
  CONTENT_WIDTH,
  GetLocalVideoUrlType
} from '../../../../constant';
import Log from '../../../../../../common/log';
import {
  PlayerStatus,
  Progress
} from '../../../../../../components/MediaPlayer/index.d';
import Loading from '../Loading';
import { DOWNLOAD_VIDEO_TASK_CAN_PLAY } from '../../../../../../util/videoDownload/constant';

const TAG = 'ClassRoomVideo';

interface VideoProps {
  url: string;
  setTotalTime?: (value: number) => void;
  onProgress: (e: Progress) => void;
  onEnd: () => void;
}

const Video = (props: VideoProps, ref: any): ReactElement => {
  const [havePrepare, setHavePrepare] = useState(false);
  const [isPrepare, setIsPrepare] = useState(false);
  const [localUrl, setLocalUrl] = useState('');
  const [localTotalTime, setLocalTotalTime] = useState(0);
  const [showLoading, setShowLoading] = useState(false);

  const localResolve = useRef((data?: any): void => {});
  const videoPlayer = useRef<any>(null);

  const { url, setTotalTime, onProgress, onEnd } = props;

  const videoMute = useCallback((mute: boolean): void => {
    if (videoPlayer && videoPlayer.current) {
      videoPlayer.current.setMute(mute);
    }
  }, []);

  const play = useCallback((): void => {
    if (videoPlayer && videoPlayer.current) {
      videoPlayer.current.start();
    }
  }, []);

  const setVolume = useCallback((v: number): void => {
    if (videoPlayer && videoPlayer.current) {
      videoPlayer.current.setVolume(v);
    }
  }, []);

  const pause = useCallback((): void => {
    Log.i(
      TAG,
      JSON.stringify({
        event: 'pause',
        url
      })
    );
    if (videoPlayer && videoPlayer.current) {
      videoPlayer.current.pause();
    }
  }, [url]);

  const onPrepare = useCallback(
    (value: PlayerStatus): void => {
      Log.i(
        TAG,
        JSON.stringify({
          event: 'onPrepare',
          url
        })
      );
      // setVolume(0.5);
      setHavePrepare(true);
      localResolve.current(value.duration * 1000);
    },
    [url]
  );

  const onSeekComplete = useCallback((): void => {
    localResolve.current();
  }, []);

  const onLoad = useCallback(
    (value: PlayerStatus): void => {
      const { duration } = value;
      if (setTotalTime) {
        setTotalTime(duration * 1000);
      }
      setLocalTotalTime(duration * 1000);
    },
    [setTotalTime]
  );

  // 初始化播放器
  const prepare = (): Promise<number> =>
    new Promise((resolve, reject): void => {
      localResolve.current = resolve;
      Log.i(
        TAG,
        JSON.stringify({
          event: 'prepare',
          havePrepare,
          url
        })
      );
      if (havePrepare) {
        resolve(localTotalTime);
        return;
      }
      setIsPrepare(true);
    });

  // 同步播放器数据到房间 1. totalTime; 2.videoPlayer;
  const start = (mute = false): Promise<void> =>
    new Promise((resolve, reject): void => {
      Log.i(
        TAG,
        JSON.stringify({
          event: 'start',
          url
        })
      );
      videoMute(mute);
      play();
      resolve();
    });

  const seek = (time: number): Promise<void> =>
    new Promise((resolve, reject): void => {
      Log.i(
        TAG,
        JSON.stringify({
          event: 'seek',
          url,
          time
        })
      );
      localResolve.current = resolve;
      if (videoPlayer && videoPlayer.current) {
        const seekTime = time - 500 < 0 ? time : time - 500;
        if (localTotalTime > 0 && localTotalTime < seekTime) {
          localResolve.current();
          Log.e(
            TAG,
            JSON.stringify({
              event: 'seek unValid time',
              url,
              seekTime
            })
          );
          return;
        }
        videoPlayer.current.seek(seekTime);
      }
    });

  const destroy = (): void => {
    setIsPrepare(false);
    setHavePrepare(false);
    setShowLoading(false);
  };

  useImperativeHandle(ref, (): { [key: string]: any } => {
    return {
      prepare,
      start,
      pause,
      seek,
      destroy,
      mute: videoMute,
      setVolume
    };
  });

  const reportError = useCallback((reason: string, videoUrl: string): void => {
    reportVideoPlayerOnError(reason, videoUrl);
  }, []);

  const changeLocalUrl = useCallback(
    (res: any): void => {
      const { urls } = res;
      if (url.length > 0 && localUrl.length === 0 && urls.includes(url)) {
        Log.i(
          TAG,
          JSON.stringify({
            event: 'classRoomVideoDownloadVideoSuccess',
            url
          })
        );
        setLocalUrl(getLocalVideoUrl(url, GetLocalVideoUrlType.Origin));
      }
    },
    [localUrl.length, url]
  );

  useEffect((): (() => void) => {
    const downloadSuccessSubscription = DeviceEventEmitter.addListener(
      DOWNLOAD_VIDEO_TASK_CAN_PLAY,
      (res): void => {
        if (!res) return;
        changeLocalUrl(res);
      }
    );
    return (): void => {
      if (downloadSuccessSubscription) {
        downloadSuccessSubscription.remove();
      }
    };
  }, [changeLocalUrl]);

  useEffect((): void => {
    if (url.length > 0) {
      const newLocalUrl = getLocalVideoUrl(url, GetLocalVideoUrlType.Origin);
      setLocalUrl(newLocalUrl);
    }
  }, [url]);

  useEffect((): void => {
    if (!isPrepare) return;
    if (url.length > 0 && localUrl.length === 0) {
      Log.i(
        TAG,
        JSON.stringify({
          event: 'playErrorStartDownload',
          url,
          localUrl
        })
      );
      setShowLoading(true);
      return;
    }
    setShowLoading((loading): boolean => {
      if (loading) {
        Log.i(
          TAG,
          JSON.stringify({
            event: 'playErrorEndDownload',
            url,
            localUrl
          })
        );
      }
      return false;
    });
  }, [isPrepare, localUrl, url]);

  return useMemo(
    (): ReactElement => (
      <View style={styles.container}>
        {localUrl !== '' && isPrepare && (
          <MediaPlayer
            style={styles.video}
            size={{ width: CONTENT_WIDTH, height: CARD_HEIGHT }}
            src={localUrl}
            ref={videoPlayer}
            onProgress={onProgress}
            onEnd={onEnd}
            onLoad={onLoad}
            onPrepare={onPrepare}
            onSeekComplete={onSeekComplete}
            mute
            onBufferStart={(): void => {
              setShowLoading(true);
            }}
            onBufferEnd={(): void => {
              setShowLoading(false);
            }}
            onError={(error): void => {
              Log.e(
                TAG,
                JSON.stringify({
                  event: 'onError',
                  error,
                  url,
                  videoUrl: localUrl
                })
              );
              reportError(
                JSON.stringify({
                  tag: TAG,
                  event: 'onError',
                  error,
                  url,
                  videoUrl: localUrl
                }),
                url
              );
            }}
          />
        )}
        {showLoading && <Loading />}
      </View>
    ),
    [
      isPrepare,
      localUrl,
      onPrepare,
      onSeekComplete,
      onEnd,
      onLoad,
      onProgress,
      reportError,
      showLoading,
      url
    ]
  );
};

export default forwardRef(Video);
